package com.wangzhen.abstractroutingdatasource.config;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.CollectionUtils;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 多数据源配置
 */
@Configuration
public class DataSourceConfig {

    @Autowired
    private DataSourceProperties dataSourceProperties;

    @Value("${mybatis.typeAliasesPackage}")
    private String typeAliasesPackage;

    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;

    @Bean
    public List<DataSource> masterDatasources () throws Exception {
        final List<Map<String, String>> masters = dataSourceProperties.getMasters();
        if (CollectionUtils.isEmpty(masters)) {
            throw new IllegalArgumentException("至少需要一个主数据源");
        }
        final List<DataSource> dataSources = new ArrayList<>();
        for (Map map : masters) {
            dataSources.add(DruidDataSourceFactory.createDataSource(map));
        }
        return dataSources;
    }

    @Bean
    public List<DataSource> slaveDatasources () throws Exception {
        final List<Map<String, String>> slaves = dataSourceProperties.getSlaves();
        if (CollectionUtils.isEmpty(slaves)) {
            throw new IllegalArgumentException("至少需要一个从数据源");
        }
        final List<DataSource> dataSources = new ArrayList<>();
        for (Map map : slaves) {
            dataSources.add(DruidDataSourceFactory.createDataSource(map));
        }
        return dataSources;
    }

    @Bean
    @Primary
    @DependsOn({"masterDatasources", "slaveDatasources"})
    public MyRoutingDataSource routingDataSource () throws Exception{
        final Map<Object, Object> targetDataSources = new HashMap<>();

        for (int i = 0; i < masterDatasources().size(); i++) {
            targetDataSources.put(DataSourceContextHolder.WRITE + i, masterDatasources().get(i));
        }
        for (int i = 0; i < slaveDatasources().size(); i++) {
            targetDataSources.put(DataSourceContextHolder.READ + i, slaveDatasources().get(i));
        }

        final MyRoutingDataSource routingDataSource = new MyRoutingDataSource();
        // 该方法是AbstractRoutingDataSource的方法
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(masterDatasources().get(0));
        return routingDataSource;
    }

    /**
     * 多数据源需要自己设置sqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory (final MyRoutingDataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);// 指定数据源
        sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);// 指定基包
        final ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * 设置事务，事务需要知道当前使用的是哪个数据源才能进行事务处理
     * @return
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager() throws Exception {
        return new DataSourceTransactionManager(routingDataSource());
    }
}

